import os
from absl import app, flags
import json
import logging
import tempfile

from google.protobuf.json_format import MessageToDict, ParseDict

from coin2.strategy.dmm_strat.dmm_strat_config_pb2 import (
    DmmProductConfig, DmmStrategyConfig)
from coin2.app.driver_pb2 import DriverConfig


FLAGS = flags.FLAGS


def _validate_driver_config_proto(config_proto):
  assert isinstance(config_proto, DriverConfig)
  assert config_proto.strategy.HasField('strategy_name')
  assert config_proto.strategy.HasField('strategy_group')


def _gen_strat_config_proto(config_path):
  with open(config_path) as fread:
    config = json.load(fread)
  strat_config_proto = DmmStrategyConfig()
  ParseDict(config, strat_config_proto)
  product_configs = []
  for product_config in strat_config_proto.product_configs:
    product_config_proto = DmmProductConfig()
    product_config_proto.CopyFrom(strat_config_proto.common_config)
    product_config_proto.MergeFrom(product_config)
    product_configs.append(product_config_proto)
  strat_config_proto.ClearField('product_configs')
  strat_config_proto.product_configs.MergeFrom(product_configs)
  return strat_config_proto


def _gen_feed_manager_config(strat_config):
  feed_manager_config = {}
  feed_mea_map = {}
  enable_telemetry = True if strat_config.get('enable_telemetry') else False
  for product_config in strat_config['product_configs']:
    mea = product_config['trade_mea']
    if mea in feed_mea_map:
      assert feed_mea_map[mea]['recipe'] == product_config['recipe']
      feed_mea_map[mea]['products']['norms'].append(product_config['trade_symbol'])
    else:
      feed_system_config = {}
      if enable_telemetry:
        feed_system_config['raw_parser'] = {
          'enable_telemetry': True
        }
        feed_system_config['message_counter'] = True
      feed_system_config['mea'] = product_config['trade_mea']
      feed_system_config['type'] = 'FEED_SYSTEM_TYPE_LIVE'
      feed_system_config['products'] = {'norms': [product_config['trade_symbol']]}
      feed_system_config['websocket'] = {'num_workers': 1}
      feed_system_config['recipe'] = product_config['recipe']
      feed_mea_map[mea] = feed_system_config
  feed_manager_config['exchanges'] = feed_mea_map
  return feed_manager_config


def _gen_order_manager_config(strat_config, key_filepath):
  order_manager_config = {}
  order_exchange_map = {}
  for product_config in strat_config['product_configs']:
    splitted = product_config['trade_mea'].split('.')
    assert len(splitted) == 3, splitted
    exchange = splitted[1]
    if exchange in order_exchange_map:
      order_exchange_map[exchange]['products']['norms'].append(
          product_config['trade_symbol'])
    else:
      order_system_config = {}
      order_system_config['mea'] = product_config['trade_mea']
      order_system_config['type'] = 'ORDER_SYSTEM_TYPE_LIVE_OG'
      order_system_config['products'] = {'norms': [product_config['trade_symbol']]}
      order_system_config['order_gateway_config'] = {
          'log_private_exchange_message': False}
      order_system_config['connection_config'] = {
          'key_filepath': os.path.abspath(key_filepath),
          'use_cross_mode': True
      }
      order_exchange_map[exchange] = order_system_config
  order_manager_config['exchanges'] = order_exchange_map
  return order_manager_config


def _gen_driver_config_proto(strat_config_proto, key_file_path):
  strat_config = MessageToDict(strat_config_proto, preserving_proto_field_name=True)
  feed_manager_config = _gen_feed_manager_config(strat_config)
  order_manager_config = _gen_order_manager_config(strat_config, key_file_path)
  driver_config = {
      'log': {'health_report_period_ns': 30000000000},
      'strategy': strat_config['strategy_request'],
      'feed': feed_manager_config,
      'order': order_manager_config,
      'executor': {'live': {'parser_threads': 3}},
  }
  driver_config_proto = DriverConfig()
  ParseDict(driver_config, driver_config_proto)
  return driver_config_proto


def _gen_dmm_config(config_path, key_file_path):
  strat_config_proto = _gen_strat_config_proto(config_path)
  driver_config_proto = _gen_driver_config_proto(strat_config_proto, key_file_path)
  _validate_driver_config_proto(driver_config_proto)
  return driver_config_proto, strat_config_proto


def _dump_output_config(driver_config_proto, strat_config_proto,
                        out_driver_json=None, out_dmm_strat_json=None):
  strategy_name = strat_config_proto.strategy_request.strategy_name
  if out_driver_json is None:
    out_driver_json = tempfile.mkstemp(
        dir='/tmp', prefix=f'{strategy_name}.driver_config.', suffix='.json')[-1]
  with open(out_driver_json, 'w') as fwrite:
    json.dump(MessageToDict(driver_config_proto, preserving_proto_field_name=True),
              fwrite, indent=2, sort_keys=True)

  if out_dmm_strat_json is None:
    out_dmm_strat_json = tempfile.mkstemp(
        dir='/tmp', prefix=f'{strategy_name}.strat_config.', suffix='.json')[-1]
  with open(out_dmm_strat_json, 'w') as fwrite:
    json.dump(MessageToDict(strat_config_proto, preserving_proto_field_name=True),
              fwrite, indent=2, sort_keys=True)

  print(os.path.abspath(out_driver_json))
  print(os.path.abspath(out_dmm_strat_json))


def main(_):
  config_path = FLAGS.config_path
  key_filepath = FLAGS.key_filepath
  assert os.path.exists(config_path), config_path
  driver_config_proto, strat_config_proto = _gen_dmm_config(config_path, key_filepath)
  _dump_output_config(driver_config_proto, strat_config_proto,
                      FLAGS.out_driver_json, FLAGS.out_dmm_strat_json)


if __name__ == '__main__':
  logging.basicConfig(
      level='DEBUG', format='%(levelname)8s %(asctime)s %(name)s] %(message)s')
  flags.DEFINE_string('config_path', None, '')
  flags.DEFINE_string('key_filepath', None, '')
  flags.DEFINE_string('out_driver_json', None, '')
  flags.DEFINE_string('out_dmm_strat_json', None, '')
  app.run(main)
